package com.atguigu.gmall.order.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.cart.feign.CartFeign;
import com.atguigu.gmall.model.cart.CartInfo;
import com.atguigu.gmall.model.enums.OrderStatus;
import com.atguigu.gmall.model.enums.ProcessStatus;
import com.atguigu.gmall.model.order.OrderDetail;
import com.atguigu.gmall.model.order.OrderInfo;
import com.atguigu.gmall.model.payment.PaymentInfo;
import com.atguigu.gmall.order.mapper.OrderDetailMapper;
import com.atguigu.gmall.order.mapper.OrderInfoMapper;
import com.atguigu.gmall.order.mapper.PaymentInfoMapper;
import com.atguigu.gmall.order.service.OrderService;
import com.atguigu.gmall.order.util.OrderThreadLocalUtil;
import com.atguigu.gmall.payment.feign.WxPayFeign;
import com.atguigu.gmall.payment.feign.ZfbPayFeign;
import com.atguigu.gmall.product.feign.ProductFeign;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * projectName: gmall
 *
 * @author: 陈鹏
 * time: 2022/10/10 19:28 星期一
 * description:
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class OrderServiceImpl implements OrderService {

    @Autowired
    private CartFeign cartFeign;

    @Resource
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private ProductFeign productFeign;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 新增普通订单
     *
     * @param orderInfo
     */
    @Override
    public void addOrder(OrderInfo orderInfo) {
        //参数校验
        if(orderInfo == null){
            return;
        }
        //获取用户名
        String username = OrderThreadLocalUtil.get();
        //设置用户下单的计数器
        Long increment = redisTemplate.opsForValue().increment("User_Add_Order_Cart_" + username, 1);
        if(increment > 1){
            return;
        }
        try{
            //设置这个标识位3秒失效  工作中通常设置10秒
            redisTemplate.expire("User_Add_Order_Cart_" + username,3, TimeUnit.SECONDS);

            //远程调用购物车微服务 查询本次购买的全部数据
            Map<String, Object> result = cartFeign.getAddOrderCart();
            //System.out.println("result = " + result);
            //获取总金额
            Double totalAmount =
                    Double.valueOf(result.get("totalMoney").toString());
            //补全orderInfo信息
            orderInfo.setTotalAmount(new BigDecimal(totalAmount+""));
            orderInfo.setOrderStatus(OrderStatus.UNPAID.getComment());
            orderInfo.setUserId(username);
            orderInfo.setCreateTime(new Date());
            orderInfo.setExpireTime(new Date(System.currentTimeMillis()+1800000));
            orderInfo.setProcessStatus(ProcessStatus.UNPAID.getComment());
            //保存orderInfo -- service-order (事务一 本地事务)
            int insert = orderInfoMapper.insert(orderInfo);
            if(insert <= 0){
                throw new RuntimeException("新增订单失败!");
            }
            //获取订单号
            Long orderId = orderInfo.getId();
            //保存订单的详情信息 -- service-order (事务一 本地事务)
            List cartInfoList = (List)result.get("cartInfoList");
            Map<String, Object> decountMap = saveOrderDetail(cartInfoList, orderId);
            //清空本次购买的购物车信息 -- service-cart(事务二,本地事务)
            cartFeign.removeCheckCart();
            //扣减库存 -- service-product (事务三,本地事务)
            productFeign.decount(decountMap);
            //计时:30分钟内若没有支付则需要取消订单,发送延迟消息
            rabbitTemplate.convertAndSend("order_normal_exchange",
                    "order.dead",
                    orderId + "",
                    (message -> {
                        //获取消息属性
                        MessageProperties messageProperties = message.getMessageProperties();
                        //设置过期时间
                        messageProperties.setExpiration("1800000");
                        //返回
                        return message;
                    }));

        }catch (Exception e){
            throw new RuntimeException("新增订单失败!!");
        }finally {
            redisTemplate.delete("User_Add_Order_Cart_" + username);
        }
    }

    @Resource
    private OrderDetailMapper orderDetailMapper;

    /**
     * 保存订单详情
     * @param cartInfoList
     * @param orderId
     * @return
     */
    private Map<String, Object> saveOrderDetail(List cartInfoList, Long orderId) {
        //需要扣减商品的id和库存
        ConcurrentHashMap<String, Object> decountMap = new ConcurrentHashMap<>();
        //遍历保存
        cartInfoList.stream().forEach(o -> {
            //序列化
            String s = JSONObject.toJSONString(o);
            //反序列化
            CartInfo cartInfo = JSONObject.parseObject(s, CartInfo.class);
            //初始化订单详情对象
            OrderDetail orderDetail = new OrderDetail();
            orderDetail.setOrderId(orderId);
            orderDetail.setSkuId(cartInfo.getSkuId());
            orderDetail.setSkuName(cartInfo.getSkuName());
            orderDetail.setImgUrl(cartInfo.getImgUrl());
            orderDetail.setOrderPrice(
                    cartInfo.getSkuPrice().multiply(
                            new BigDecimal(cartInfo.getSkuNum())));
            orderDetail.setSkuNum(cartInfo.getSkuNum());
            //保存
            int insert = orderDetailMapper.insert(orderDetail);
            if(insert <=0){
                throw new RuntimeException("新增订单详情失败!");
            }
            decountMap.put(cartInfo.getSkuId() + "", cartInfo.getSkuNum());
        });
        //返回
        return decountMap;
    }

    /**
     * 取消订单 超时 、用户主动取消
     *
     * @param orderId
     */
    @Override
    public void cancelOrder(Long orderId) {
        //参数校验
        if(orderId == null){
            throw new RuntimeException("取消订单失败,参数错误");
        }
        //从本地线程对象中获取用户名
        String username = OrderThreadLocalUtil.get();
        //构建条件查询器 准备查询订单
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        //设置订单号作为查询条件
        wrapper.eq(OrderInfo::getId,orderId);
        //用户名不为空设置为查询条件
        if(!StringUtils.isEmpty(username)){
            wrapper.eq(OrderInfo::getUserId,username);
        }
        //查询
        OrderInfo orderInfo = orderInfoMapper.selectOne(wrapper);
        if(orderId == null ||
                orderInfo.getId() == null ||
                !orderInfo.getOrderStatus().equals(OrderStatus.UNPAID.getComment())){
            throw new RuntimeException("取消订单失败,订单不存在!");
        }

        //查询用户订单的支付渠道
        PaymentInfo paymentInfo =
                paymentInfoMapper.selectOne(
                        new LambdaQueryWrapper<PaymentInfo>()
                                .eq(PaymentInfo::getOrderId, orderId));
        if(paymentInfo != null && paymentInfo.getId() != null){
            //需要关闭交易,获取支付渠道
            String paymentType = paymentInfo.getPaymentType();
            if(paymentType.equals("WX")){
                //微信关闭---TODO--作业

            }else if(paymentType.equals("ZFB")){
                //支付宝关闭---TODO--作业

            }else{
                //其他关闭

            }
        }

        //判断是哪种取消
        if(StringUtils.isEmpty(username)){
            //超时取消
            int result = orderInfoMapper.timeOutOrder(OrderStatus.TIMEOUT.getComment(), orderId, OrderStatus.UNPAID.getComment());
            if(result <= 0){
                return;
            }
        }else {
            //用户主动取消
            int result = orderInfoMapper.cancelOrder(OrderStatus.CANCEL.getComment(), orderId, OrderStatus.UNPAID.getComment(), username);
            if(result < 0){
                throw new RuntimeException("主动取消订单失败,请重试!");
            }
        }
        //回滚库存
        rollbackStock(orderId);
    }

    /**
     * 回滚库存
     * @param orderId
     */
    private void rollbackStock(Long orderId) {
        //查询
        List<OrderDetail> orderDetails = orderDetailMapper.selectList(
                new LambdaQueryWrapper<OrderDetail>().
                        eq(OrderDetail::getOrderId, orderId));
        //容器初始化
        ConcurrentHashMap<String, Object> rollBackMap = new ConcurrentHashMap<>();
        //需要回滚的商品Id和数量
        orderDetails.stream().forEach(orderDetail -> {
            //商品Id
            Long skuId = orderDetail.getSkuId();
            //商品数量
            Integer skuNum = orderDetail.getSkuNum();
            //放入rollBackMap
            rollBackMap.put(skuId+"",skuNum);
        });
        //回滚库存
        productFeign.rollback(rollBackMap);
    }

    /**
     * 修改订单支付结果
     *
     * @param result
     */
    @Override
    public void updateOrder(String result) {
        //将支付结果反序列化为map
        Map<String,String> payResultMap = JSONObject.parseObject(result, Map.class);
        //判断支付渠道
        String payWay = payResultMap.get("pay_way");
        //获取订单号
        String orderId = payResultMap.get("out_trade_no");
        //第三方流水号初始化
        String outTradeNo = "";
        //判断交易渠道
        if(payWay.equals("WX")){
            //微信支付交易号
            outTradeNo = payResultMap.get("transaction_id");
        }else {
            //支付宝支付交易号
            outTradeNo = payResultMap.get("trade_no");
        }
        //修改订单
        updatePayOrder(result,outTradeNo,orderId);
    }

    /**
     * 修改订单的支付状态
     * @param tradeBody
     * @param outTradeNo
     * @param orderId
     */
    public void updatePayOrder(String tradeBody,String outTradeNo,String orderId){
        //查询订单
        OrderInfo orderInfo = orderInfoMapper.selectOne(new LambdaQueryWrapper<OrderInfo>().
                eq(OrderInfo::getId, orderId).
                eq(OrderInfo::getOrderStatus, OrderStatus.UNPAID.getComment()));
        //判断
        if(orderInfo == null || orderInfo.getId() == null){
            return;
        }
        //只修改订单为未支付的
        orderInfo.setOutTradeNo(outTradeNo);
        orderInfo.setTradeBody(tradeBody);
        orderInfo.setOrderStatus(OrderStatus.PAID.getComment());
        orderInfo.setProcessStatus(ProcessStatus.PAID.getComment());
        //修改订单
        int update = orderInfoMapper.updateById(orderInfo);
        if(update <= 0){
            throw new RuntimeException("修改订单的支付状态失败");
        }
    }

    @Autowired
    private WxPayFeign wxPayFeign;

    @Autowired
    private ZfbPayFeign zfbPayFeign;
    /**
     * 获取支付信息
     *
     * @param orderId
     * @param payWay
     * @return
     */
    @Override
    public String getPayInfo(String orderId, String payWay) {

        //参数校验
        if(StringUtils.isEmpty(orderId) ||
                StringUtils.isEmpty(payWay)){
            throw new RuntimeException("参数错误!!");
        }

        //控制: 查看用户是否在某个平台已经创建过支付渠道---关闭的最短间隔都是5分钟
        Long increment = redisTemplate.opsForValue().increment("Order_Pay_Count_" + orderId, 1);
        //加锁:锁的粒度是每笔订单
        if(increment == 1){
            //接下来5分钟不允许更换支付渠道
            redisTemplate.expire("Order_Pay_Count_" + orderId,5,TimeUnit.MINUTES);
            //获取用户名
            String username = OrderThreadLocalUtil.get();
            //查询订单详细信息
            OrderInfo orderInfo = orderInfoMapper.selectOne(new LambdaQueryWrapper<OrderInfo>().
                    eq(OrderInfo::getId, orderId).
                    eq(OrderInfo::getUserId, username).
                    eq(OrderInfo::getOrderStatus, OrderStatus.UNPAID.getComment()));
            if(orderInfo == null || orderInfo.getId() == null){
                throw new RuntimeException("没有未支付的订单!!");
            }
            //判断用户选择的渠道
            if(payWay.equals("Wx")){
                //微信支付,获取二维码地址 --Feign调用
                Map<String, String> result = wxPayFeign.getPayCodeUrl("深圳java0409" + username + "的" + orderId + "订单",
                        "java0409000" + orderId + "",
                        orderInfo.getTotalAmount().multiply(new BigDecimal(100)).intValue() + "");
                //记录用户的支付渠道
                addPaymentInfo(orderInfo, "WX");
                //返回结果
                return result.get("code_url");
            }else if(payWay.equals("ZFB")){
                //支付宝支付,获取支付页面 --Feign调用
                String payPage = zfbPayFeign.getPayPage("深圳java0409" + username + "的" + orderId + "订单",
                        "java0409000" + orderId + "",
                        orderInfo.getTotalAmount().doubleValue() + "");
                //记录用户的支付渠道
                addPaymentInfo(orderInfo, "ZFB");
                //返回结果
                return payPage;
            }else{
                throw new RuntimeException("支付渠道不存在!");
            }
        }else {
            throw new RuntimeException("订单正在支付中!!!!!");
        }
    }

    @Resource
    private PaymentInfoMapper paymentInfoMapper;
    /**
     * 记录用户的支付渠道信息
     * @param orderInfo
     * @param payWay
     */
    private void addPaymentInfo(OrderInfo orderInfo, String payWay) {

        //查询是否申请过支付渠道
        PaymentInfo paymentInfo =
                paymentInfoMapper.selectOne(
                        new LambdaQueryWrapper<PaymentInfo>()
                                .eq(PaymentInfo::getOrderId, orderInfo.getId()));
        if(paymentInfo == null || paymentInfo.getId() == null){
            //第一次申请支付
            paymentInfo = new PaymentInfo();
            paymentInfo.setOrderId(orderInfo.getId());
            paymentInfo.setTotalAmount(orderInfo.getTotalAmount());
            paymentInfo.setPaymentType(payWay);
            paymentInfo.setCreateTime(new Date());
            //保存
            int insert = paymentInfoMapper.insert(paymentInfo);
            if(insert <= 0){
                throw new RuntimeException("新增交易渠道失败!!");
            }
        }else{
            //不是第一次
            paymentInfo.setPaymentType(payWay);
            paymentInfo.setCreateTime(new Date());
            //保存
            int insert = paymentInfoMapper.updateById(paymentInfo);
            if(insert <= 0){
                throw new RuntimeException("修改交易渠道失败!!");
            }
        }
    }
}
